#define CHECK_NumberVec3

using System;

namespace FixedPointMath
{
    [Serializable]
    public struct Vector3
    {
        public Number x;

        public Number y;

        public Number z;

        public static readonly Vector3 Zero = new Vector3(Number.Zero, Number.Zero, Number.Zero);

        public static readonly Vector3 One = new Vector3(Number.N1, Number.N1, Number.N1);

        public static readonly Vector3 Forward = new Vector3(Number.Zero, Number.Zero, Number.N1);
        public static readonly Vector3 Backward = new Vector3(Number.Zero, Number.Zero, Number.NN1);

        public static readonly Vector3 Up = new Vector3(Number.Zero, Number.N1, Number.Zero);
        public static readonly Vector3 Down = new Vector3(Number.Zero, Number.NN1, Number.Zero);

        public static readonly Vector3 Right = new Vector3(Number.N1, Number.Zero, Number.Zero);
        public static readonly Vector3 Left = new Vector3(Number.NN1, Number.Zero, Number.Zero);

        public Vector3(Number _x, Number _y, Number _z)
        {
            this.x = _x;
            this.y = _y;
            this.z = _z;
        }

        #region 常用方法、属性
        /// <summary>
        /// 以下标形式处理字段
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public Number this[int index]
        {
            get
            {
#if CHECK_NumberVec3
                if (index < 0 || index > 2)
                {
                    throw new Exception("NumberVec3 public Number this[int index] get index invalid");
                }
#endif
                return index == 0 ? this.x : (index == 1 ? this.y : this.z);
            }
            set
            {
#if CHECK_NumberVec3
                if (index < 0 || index > 2)
                {
                    throw new Exception("NumberVec3 public Number this[int index] set index invalid");
                }
#endif
                if (index == 0)
                {
                    this.x = value;
                }
                else if (index == 1)
                {
                    this.y = value;
                }
                else
                {
                    this.z = value;
                }
            }
        }

        /// <summary>
        /// 单位向量
        /// </summary>
        public Vector3 normalized
        {
            get
            {
                Vector3 v = Zero;
                if (this.x != Number.Zero || this.y != Number.Zero || this.z != Number.Zero)
                {
                    var len = this.magnitude;
                    v.x /= len;
                    v.y /= len;
                    v.z /= len;
                }
                return v;
            }
        }

        /// <summary>
        /// 平方根
        /// </summary>
        public Number magnitude
        {
            get
            {
                return (this.x * this.x + this.y * this.y + this.z * this.z).Sqrt();
            }
        }

        /// <summary>
        /// 根的平方
        /// </summary>
        public Number squareMagnitude
        {
            get
            {
                return this.x * this.x + this.y * this.y + this.z * this.z;
            }
        }

        public void Set(Number _x, Number _y, Number _z)
        {
            x = _x;
            y = _y;
            z = _z;
        }

        /// <summary>
        /// 单位向量
        /// </summary>
        /// <returns></returns>
        public void Normalize()
        {
            if (this.x != Number.Zero || this.y != Number.Zero || this.z != Number.Zero)
            {
                var len = this.magnitude;
                this.x /= len;
                this.y /= len;
                this.z /= len;
            }
        }

        /// <summary>
        /// 调整向量长度到指定长度
        /// </summary>
        /// <param name="newMagnitude"></param>
        /// <returns></returns>
        public Vector3 NormalizeTo(Number newMagnitude)
        {
#if CHECK_NumberVec3
            if (newMagnitude < 0)
            {
                throw new Exception("public NumberVec3 NormalizeTo(int newMagnitude) 不能小于0");
            }
#endif
            if (this.x != Number.Zero || this.y != Number.Zero || this.z != Number.Zero)
            {
                var ratio = newMagnitude / this.magnitude;
                this.x = this.x * ratio;
                this.y = this.y * ratio;
                this.z = this.z * ratio;
            }
            return this;
        }

        /// <summary>
        /// 从from到to, 但限定
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="magnitudeLimit"></param>
        /// <returns></returns>
        public static Vector3 MoveTowards(Vector3 from, Vector3 to, Number magnitudeLimit)
        {
            if ((to - from).squareMagnitude <= (magnitudeLimit * magnitudeLimit))
            {
                return to;
            }
            return from + (to - from).NormalizeTo(magnitudeLimit);
        }

        public Vector3 RotateY(int degree)
        {
            VFactor vFactor;
            VFactor vFactor2;
            OldMathFP.sincos(out vFactor, out vFactor2, (long)(31416 * degree), 1800000L);
            long num = vFactor2.nom * vFactor.den;
            long num2 = vFactor2.den * vFactor.nom;
            long b = vFactor2.den * vFactor.den;
            Vector3 vInt;
            vInt.x = (int)OldMathFP.Divide((long)this.x * num + (long)this.z * num2, b);
            vInt.z = (int)OldMathFP.Divide((long)(-(long)this.x) * num2 + (long)this.z * num, b);
            vInt.y = 0;
            return vInt.NormalizeTo(1000);
        }
        #endregion

        #region implement
        public override string ToString()
        {
            return string.Format("[{0} {1} {2}]", x.AsFloat(), y.AsFloat(), z.AsFloat());
        }
        public override bool Equals(object o)
        {
            if (o is Vector3)
            {
                Vector3 oth = (Vector3)o;
                return this.x == oth.x && this.y == oth.y && this.z == oth.z;
            }
            return false;
        }

        public override int GetHashCode()
        {
            return (int)(this.x * 23 * 23 + this.y * 23 + this.z);
        }
        #endregion

        #region 基本运算及比较
        public static Vector3 operator -(Vector3 lhs, Vector3 rhs)
        {
            lhs.x -= rhs.x;
            lhs.y -= rhs.y;
            lhs.z -= rhs.z;
            return lhs;
        }

        public static Vector3 operator -(Vector3 lhs)
        {
            lhs.x = -lhs.x;
            lhs.y = -lhs.y;
            lhs.z = -lhs.z;
            return lhs;
        }

        public static Vector3 operator +(Vector3 lhs, Vector3 rhs)
        {
            lhs.x += rhs.x;
            lhs.y += rhs.y;
            lhs.z += rhs.z;
            return lhs;
        }

        public static Vector3 operator *(Vector3 lhs, Number rhs)
        {
            lhs.x *= rhs;
            lhs.y *= rhs;
            lhs.z *= rhs;
            return lhs;
        }

        public static Vector3 operator /(Vector3 lhs, Number rhs)
        {
            lhs.x /= rhs;
            lhs.y /= rhs;
            lhs.z /= rhs;
            return lhs;
        }

        public static bool operator ==(Vector3 lhs, Vector3 rhs)
        {
            return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
        }

        public static bool operator !=(Vector3 lhs, Vector3 rhs)
        {
            return lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z;
        }
        #endregion
    }
}
